package org.batatasframework.strategy;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.batatasframework.common.ParameterReader;
import org.batatasframework.manager.HtmlDetailBuilder;
import org.batatasframework.manager.RuntimeManager;
import org.batatasframework.notice.NoticeContent;
import org.batatasframework.notice.RuntimeNotifier;

import cn.bonoon.core.PeriodType;

/**
 * 支持周期检查的策略；
 * 
 * 1. 定义每天关闭策略，{@link #start}
 * @author jackson
 *
 */
public class ServerStrategyPeriod extends AbstractServiceStrategyHandler implements RuntimeNotifier, RuntimeManager{

	/** 开始，需要根据类型确定 */
	private int start;
	
	/** 持续时间，这里也是以秒钟来计算的，如：持续时间2小时，则应该设置为：2*60*60 */
	private int duration;
	
	/** 周期策略的周期类型 */
	private PeriodType periodType = PeriodType.DAILY;

	private String noticeContent = "";

	/** 提前多少秒提示用户，这里用的是秒，默认的情况下是30分钟 */
	private int noticeSecond = 30 * 60;
	
	/** 控制的状态，0表示不限制，大于0表示要跳过的次数，-1表示不启用，这个是设置的值，不会改变 */
	private int stopSetting;
	
	/** 这个是运行时的值，如果限制次数的，则会累减；如：设置了暂停1次或10次限制 */
	private int stopValue;
	
	private long startAt;
	private long endAt;
	private long noticeAt;
	
	public ServerStrategyPeriod() {
		name = "周期执行";
	}
	
	private void internalResolve(){
		synchronized (this) {
			//已经超过了限制的时间范围内，需要对这里的值进行重新的计算了。
			Calendar startCal = periodType.resolve(start, duration);
			startAt = startCal.getTimeInMillis();
			endAt = startAt + duration;
			if(noticeSecond > 0){
				noticeAt = startAt - noticeSecond;
			}else{
				noticeAt = 0;
			}
			
			if(stopValue > 0) stopValue--;
		}
	}

	@Override
	public ServerStrategyResult execute(HttpServletResponse response, ServerStrategyEvent event) throws IOException, ServletException{
		
		/*
		 * 如果"stopValue"为负数，则表示关闭本服务器上的周期执行策略
		 * 这个值只有在本服务器有效
		 * 如果服务器重启这个值将无效
		 */
		if(stopValue < 0) return ServerStrategyResult.CONTINUE;
		
		long current = System.currentTimeMillis();

		if(current > endAt){
			internalResolve();
		}
		
		if(stopValue == 0 && startAt < current && current < endAt){
			
			/*
			 * 禁止访问的策略是"stopValue==0"，如果"stopValue>0"则表示需要暂停多少次
			 * 在这里生成提示界面
			 */
			String deniedAccessHtml = generateDeniedAccessHtml(event);
			response.setContentType("text/html;charset=UTF-8");
			response.getWriter().write(deniedAccessHtml);
			return ServerStrategyResult.RETURN;
		}
		
		return ServerStrategyResult.CONTINUE;
	}
	
	private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
	
  private String generateDeniedAccessHtml(ServerStrategyEvent event) {
  	  StringBuilder sb = new StringBuilder();
      sb.append("<html><head><title>服务器拒绝访问</title>");
      sb.append("<link rel='stylesheet' type='text/css' href='/res/css/layout/blue/layout.css'>");
      sb.append("</head><body style='margin:0'>");
      sb.append("<div id='MaintainBG'></div>");
      sb.append("<div id='MaintainMask'></div>");
      sb.append("<div id='Maintain'>");
      sb.append("<div id='MaintainGear'>服务器正在维护</div>");
      sb.append("<div id='MaintainStart' class='Maintain'><span>开始时间：</span><div>"+sdf.format(new Date(startAt))+"</div></div>");
      sb.append("<div id='MaintainEnd' class='Maintain'><span>结束时间：</span><div>"+sdf.format(new Date(endAt))+"</div></div></div>");
      sb.append("</body></html>");
      return sb.toString();
	}
  
	@Override
	public NoticeContent notice() {
		if(stopValue == 0 && noticeAt > 0 && !noticeContent.isEmpty()){
			long current = System.currentTimeMillis();
			if(noticeAt < current && current < startAt){
				return NoticeContent.valueOf((int)((startAt - current) / 1000), noticeContent);
			}
		}
		return null;
	}

	public void setStart(int start) {
		this.start = start;
	}

	public void setDuration(int duration) {
		this.duration = duration * 1000;
	}

	public void setNoticeSecond(int noticeSecond) {
		this.noticeSecond = noticeSecond * 1000;
	}

	public void setPeriodType(PeriodType periodType) {
		this.periodType = periodType;
	}

	public void setNoticeContent(String noticeContent) {
		this.noticeContent = noticeContent;
	}
	
	private HtmlDetailBuilder htmlBuilder = new HtmlDetailBuilder(){

		@Override
		public String getId() {
			return id;
		}

		@Override
		public String getName() {
			return name;
		}

		@Override
		public boolean isEditable() {
			return true;
		}
		
		@Override
		public String getHtml() {
			StringBuilder html = new StringBuilder();

			html.append("设置：");
			html.append("<input type='text' name='stopSetting' value='").append(stopSetting).append("'  ");
			html.append("style='width:50px;' title='负数表示停用、0表示一直启动、大于0表示暂停多少个执行周期");
			
			if(null != stopSettingSource){
				html.append(";默认值(").append(parseStop(stopSettingSource)).append(")");
			}
			html.append("！'/>；");
			
			if(stopValue < 0){
				html.append("当前策略已经停用！");
			}else if(stopValue > 0){
				html.append("当前策略将暂停").append(stopValue).append("/").append(stopSetting).append("次；");
				html.append("下个周期于！").append(sdf.format(new Date(endAt))).append("结束");
			}else{
				html.append("下个周期");
				if(noticeAt > 0 && noticeContent != null){
					html.append("将于").append(sdf.format(new Date(noticeAt))).append("提示登录用户，");
				}
				html.append("维护时间：");
				html.append(sdf.format(new Date(startAt)));
				html.append(" 至 ");
				html.append(sdf.format(new Date(endAt)));
			}
			return html.toString();
		}

	};
	
	private String parseStop(int stop){
		if(stop < 0) return "-1:停用";
		if(stop > 0) return stop + ":暂停周期";
		return "0:启用";
	}

	@Override
	public void save(ParameterReader paraReader) throws Exception {
		internalSet(paraReader.getInteger("stopSetting", 0));
	}
	
	@Override
	public void reset() throws Exception {
		if(null != stopSettingSource){
			internalSet(stopSettingSource);
		}else{
			stopValue = stopSetting;
		}
	}

	private void internalSet(int stopSetting){
		this.stopSetting = stopSetting;
		this.stopValue = stopSetting;
	}

	private Integer stopSettingSource;
	
	public void setStopSetting(int stopSetting) {
		internalSet(stopSetting);
		this.stopSettingSource = stopSetting;
	}
	
	@Override
	public HtmlDetailBuilder getHtmlBuilder() {
		return htmlBuilder;
	}

}
